home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / lib / python2.5 / idlelib / configDialog.py < prev    next >
Text File  |  2008-10-05  |  53KB  |  1,148 lines

  1. """IDLE Configuration Dialog: support user customization of IDLE by GUI
  2.  
  3. Customize font faces, sizes, and colorization attributes.  Set indentation
  4. defaults.  Customize keybindings.  Colorization and keybindings can be
  5. saved as user defined sets.  Select startup options including shell/editor
  6. and default window size.  Define additional help sources.
  7.  
  8. Note that tab width in IDLE is currently fixed at eight due to Tk issues.
  9. Refer to comments in EditorWindow autoindent code for details.
  10.  
  11. """
  12. from Tkinter import *
  13. import tkMessageBox, tkColorChooser, tkFont
  14. import string, copy
  15.  
  16. from configHandler import idleConf
  17. from dynOptionMenuWidget import DynOptionMenu
  18. from tabpage import TabPageSet
  19. from keybindingDialog import GetKeysDialog
  20. from configSectionNameDialog import GetCfgSectionNameDialog
  21. from configHelpSourceEdit import GetHelpSourceDialog
  22.  
  23. class ConfigDialog(Toplevel):
  24.  
  25.     def __init__(self,parent,title):
  26.         Toplevel.__init__(self, parent)
  27.         self.configure(borderwidth=5)
  28.         self.geometry("+%d+%d" % (parent.winfo_rootx()+20,
  29.                 parent.winfo_rooty()+30))
  30.         #Theme Elements. Each theme element key is its display name.
  31.         #The first value of the tuple is the sample area tag name.
  32.         #The second value is the display name list sort index.
  33.         self.themeElements={'Normal Text':('normal','00'),
  34.             'Python Keywords':('keyword','01'),
  35.             'Python Definitions':('definition','02'),
  36.             'Python Builtins':('builtin', '03'),
  37.             'Python Comments':('comment','04'),
  38.             'Python Strings':('string','05'),
  39.             'Selected Text':('hilite','06'),
  40.             'Found Text':('hit','07'),
  41.             'Cursor':('cursor','08'),
  42.             'Error Text':('error','09'),
  43.             'Shell Normal Text':('console','10'),
  44.             'Shell Stdout Text':('stdout','11'),
  45.             'Shell Stderr Text':('stderr','12'),
  46.             }
  47.         self.ResetChangedItems() #load initial values in changed items dict
  48.         self.CreateWidgets()
  49.         self.resizable(height=FALSE,width=FALSE)
  50.         self.transient(parent)
  51.         self.grab_set()
  52.         self.protocol("WM_DELETE_WINDOW", self.Cancel)
  53.         self.parent = parent
  54.         self.tabPages.focus_set()
  55.         #key bindings for this dialog
  56.         #self.bind('<Escape>',self.Cancel) #dismiss dialog, no save
  57.         #self.bind('<Alt-a>',self.Apply) #apply changes, save
  58.         #self.bind('<F1>',self.Help) #context help
  59.         self.LoadConfigs()
  60.         self.AttachVarCallbacks() #avoid callbacks during LoadConfigs
  61.         self.wait_window()
  62.  
  63.     def CreateWidgets(self):
  64.         self.tabPages = TabPageSet(self,
  65.                 pageNames=['Fonts/Tabs','Highlighting','Keys','General'])
  66.         self.tabPages.ChangePage()#activates default (first) page
  67.         frameActionButtons = Frame(self)
  68.         #action buttons
  69.         self.buttonHelp = Button(frameActionButtons,text='Help',
  70.                 command=self.Help,takefocus=FALSE)
  71.         self.buttonOk = Button(frameActionButtons,text='Ok',
  72.                 command=self.Ok,takefocus=FALSE)
  73.         self.buttonApply = Button(frameActionButtons,text='Apply',
  74.                 command=self.Apply,takefocus=FALSE)
  75.         self.buttonCancel = Button(frameActionButtons,text='Cancel',
  76.                 command=self.Cancel,takefocus=FALSE)
  77.         self.CreatePageFontTab()
  78.         self.CreatePageHighlight()
  79.         self.CreatePageKeys()
  80.         self.CreatePageGeneral()
  81.         self.buttonHelp.pack(side=RIGHT,padx=5,pady=5)
  82.         self.buttonOk.pack(side=LEFT,padx=5,pady=5)
  83.         self.buttonApply.pack(side=LEFT,padx=5,pady=5)
  84.         self.buttonCancel.pack(side=LEFT,padx=5,pady=5)
  85.         frameActionButtons.pack(side=BOTTOM)
  86.         self.tabPages.pack(side=TOP,expand=TRUE,fill=BOTH)
  87.  
  88.     def CreatePageFontTab(self):
  89.         #tkVars
  90.         self.fontSize=StringVar(self)
  91.         self.fontBold=BooleanVar(self)
  92.         self.fontName=StringVar(self)
  93.         self.spaceNum=IntVar(self)
  94.         self.editFont=tkFont.Font(self,('courier',10,'normal'))
  95.         ##widget creation
  96.         #body frame
  97.         frame=self.tabPages.pages['Fonts/Tabs']['page']
  98.         #body section frames
  99.         frameFont=Frame(frame,borderwidth=2,relief=GROOVE)
  100.         frameIndent=Frame(frame,borderwidth=2,relief=GROOVE)
  101.         #frameFont
  102.         labelFontTitle=Label(frameFont,text='Set Base Editor Font')
  103.         frameFontName=Frame(frameFont)
  104.         frameFontParam=Frame(frameFont)
  105.         labelFontNameTitle=Label(frameFontName,justify=LEFT,
  106.                 text='Font :')
  107.         self.listFontName=Listbox(frameFontName,height=5,takefocus=FALSE,
  108.                 exportselection=FALSE)
  109.         self.listFontName.bind('<ButtonRelease-1>',self.OnListFontButtonRelease)
  110.         scrollFont=Scrollbar(frameFontName)
  111.         scrollFont.config(command=self.listFontName.yview)
  112.         self.listFontName.config(yscrollcommand=scrollFont.set)
  113.         labelFontSizeTitle=Label(frameFontParam,text='Size :')
  114.         self.optMenuFontSize=DynOptionMenu(frameFontParam,self.fontSize,None,
  115.             command=self.SetFontSample)
  116.         checkFontBold=Checkbutton(frameFontParam,variable=self.fontBold,
  117.             onvalue=1,offvalue=0,text='Bold',command=self.SetFontSample)
  118.         frameFontSample=Frame(frameFont,relief=SOLID,borderwidth=1)
  119.         self.labelFontSample=Label(frameFontSample,
  120.                 text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]',
  121.                 justify=LEFT,font=self.editFont)
  122.         #frameIndent
  123.         frameIndentSize=Frame(frameIndent)
  124.         labelSpaceNumTitle=Label(frameIndentSize, justify=LEFT,
  125.                                  text='Python Standard: 4 Spaces!')
  126.         self.scaleSpaceNum=Scale(frameIndentSize, variable=self.spaceNum,
  127.                                  label='Indentation Width', orient='horizontal',
  128.                                  tickinterval=2, from_=2, to=16)
  129.         #widget packing
  130.         #body
  131.         frameFont.pack(side=LEFT,padx=5,pady=10,expand=TRUE,fill=BOTH)
  132.         frameIndent.pack(side=LEFT,padx=5,pady=10,fill=Y)
  133.         #frameFont
  134.         labelFontTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
  135.         frameFontName.pack(side=TOP,padx=5,pady=5,fill=X)
  136.         frameFontParam.pack(side=TOP,padx=5,pady=5,fill=X)
  137.         labelFontNameTitle.pack(side=TOP,anchor=W)
  138.         self.listFontName.pack(side=LEFT,expand=TRUE,fill=X)
  139.         scrollFont.pack(side=LEFT,fill=Y)
  140.         labelFontSizeTitle.pack(side=LEFT,anchor=W)
  141.         self.optMenuFontSize.pack(side=LEFT,anchor=W)
  142.         checkFontBold.pack(side=LEFT,anchor=W,padx=20)
  143.         frameFontSample.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH)
  144.         self.labelFontSample.pack(expand=TRUE,fill=BOTH)
  145.         #frameIndent
  146.         frameIndentSize.pack(side=TOP,padx=5,pady=5,fill=BOTH)
  147.         labelSpaceNumTitle.pack(side=TOP,anchor=W,padx=5)
  148.         self.scaleSpaceNum.pack(side=TOP,padx=5,fill=X)
  149.         return frame
  150.  
  151.     def CreatePageHighlight(self):
  152.         self.builtinTheme=StringVar(self)
  153.         self.customTheme=StringVar(self)
  154.         self.fgHilite=BooleanVar(self)
  155.         self.colour=StringVar(self)
  156.         self.fontName=StringVar(self)
  157.         self.themeIsBuiltin=BooleanVar(self)
  158.         self.highlightTarget=StringVar(self)
  159.         ##widget creation
  160.         #body frame
  161.         frame=self.tabPages.pages['Highlighting']['page']
  162.         #body section frames
  163.         frameCustom=Frame(frame,borderwidth=2,relief=GROOVE)
  164.         frameTheme=Frame(frame,borderwidth=2,relief=GROOVE)
  165.         #frameCustom
  166.         self.textHighlightSample=Text(frameCustom,relief=SOLID,borderwidth=1,
  167.             font=('courier',12,''),cursor='hand2',width=21,height=10,
  168.             takefocus=FALSE,highlightthickness=0,wrap=NONE)
  169.         text=self.textHighlightSample
  170.         text.bind('<Double-Button-1>',lambda e: 'break')
  171.         text.bind('<B1-Motion>',lambda e: 'break')
  172.         textAndTags=(('#you can click here','comment'),('\n','normal'),
  173.             ('#to choose items','comment'),('\n','normal'),('def','keyword'),
  174.             (' ','normal'),('func','definition'),('(param):','normal'),
  175.             ('\n  ','normal'),('"""string"""','string'),('\n  var0 = ','normal'),
  176.             ("'string'",'string'),('\n  var1 = ','normal'),("'selected'",'hilite'),
  177.             ('\n  var2 = ','normal'),("'found'",'hit'),
  178.             ('\n  var3 = ','normal'),('list', 'builtin'), ('(','normal'),
  179.             ('None', 'builtin'),(')\n\n','normal'),
  180.             (' error ','error'),(' ','normal'),('cursor |','cursor'),
  181.             ('\n ','normal'),('shell','console'),(' ','normal'),('stdout','stdout'),
  182.             (' ','normal'),('stderr','stderr'),('\n','normal'))
  183.         for txTa in textAndTags:
  184.             text.insert(END,txTa[0],txTa[1])
  185.         for element in self.themeElements.keys():
  186.             text.tag_bind(self.themeElements[element][0],'<ButtonPress-1>',
  187.                 lambda event,elem=element: event.widget.winfo_toplevel()
  188.                 .highlightTarget.set(elem))
  189.         text.config(state=DISABLED)
  190.         self.frameColourSet=Frame(frameCustom,relief=SOLID,borderwidth=1)
  191.         frameFgBg=Frame(frameCustom)
  192.         labelCustomTitle=Label(frameCustom,text='Set Custom Highlighting')
  193.         buttonSetColour=Button(self.frameColourSet,text='Choose Colour for :',
  194.             command=self.GetColour,highlightthickness=0)
  195.         self.optMenuHighlightTarget=DynOptionMenu(self.frameColourSet,
  196.             self.highlightTarget,None,highlightthickness=0)#,command=self.SetHighlightTargetBinding
  197.         self.radioFg=Radiobutton(frameFgBg,variable=self.fgHilite,
  198.             value=1,text='Foreground',command=self.SetColourSampleBinding)
  199.         self.radioBg=Radiobutton(frameFgBg,variable=self.fgHilite,
  200.             value=0,text='Background',command=self.SetColourSampleBinding)
  201.         self.fgHilite.set(1)
  202.         buttonSaveCustomTheme=Button(frameCustom,
  203.             text='Save as New Custom Theme',command=self.SaveAsNewTheme)
  204.         #frameTheme
  205.         labelThemeTitle=Label(frameTheme,text='Select a Highlighting Theme')
  206.         labelTypeTitle=Label(frameTheme,text='Select : ')
  207.         self.radioThemeBuiltin=Radiobutton(frameTheme,variable=self.themeIsBuiltin,
  208.             value=1,command=self.SetThemeType,text='a Built-in Theme')
  209.         self.radioThemeCustom=Radiobutton(frameTheme,variable=self.themeIsBuiltin,
  210.             value=0,command=self.SetThemeType,text='a Custom Theme')
  211.         self.optMenuThemeBuiltin=DynOptionMenu(frameTheme,
  212.             self.builtinTheme,None,command=None)
  213.         self.optMenuThemeCustom=DynOptionMenu(frameTheme,
  214.             self.customTheme,None,command=None)
  215.         self.buttonDeleteCustomTheme=Button(frameTheme,text='Delete Custom Theme',
  216.                 command=self.DeleteCustomTheme)
  217.         ##widget packing
  218.         #body
  219.         frameCustom.pack(side=LEFT,padx=5,pady=10,expand=TRUE,fill=BOTH)
  220.         frameTheme.pack(side=LEFT,padx=5,pady=10,fill=Y)
  221.         #frameCustom
  222.         labelCustomTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
  223.         self.frameColourSet.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=X)
  224.         frameFgBg.pack(side=TOP,padx=5,pady=0)
  225.         self.textHighlightSample.pack(side=TOP,padx=5,pady=5,expand=TRUE,
  226.             fill=BOTH)
  227.         buttonSetColour.pack(side=TOP,expand=TRUE,fill=X,padx=8,pady=4)
  228.         self.optMenuHighlightTarget.pack(side=TOP,expand=TRUE,fill=X,padx=8,pady=3)
  229.         self.radioFg.pack(side=LEFT,anchor=E)
  230.         self.radioBg.pack(side=RIGHT,anchor=W)
  231.         buttonSaveCustomTheme.pack(side=BOTTOM,fill=X,padx=5,pady=5)
  232.         #frameTheme
  233.         labelThemeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
  234.         labelTypeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
  235.         self.radioThemeBuiltin.pack(side=TOP,anchor=W,padx=5)
  236.         self.radioThemeCustom.pack(side=TOP,anchor=W,padx=5,pady=2)
  237.         self.optMenuThemeBuiltin.pack(side=TOP,fill=X,padx=5,pady=5)
  238.         self.optMenuThemeCustom.pack(side=TOP,fill=X,anchor=W,padx=5,pady=5)
  239.         self.buttonDeleteCustomTheme.pack(side=TOP,fill=X,padx=5,pady=5)
  240.         return frame
  241.  
  242.     def CreatePageKeys(self):
  243.         #tkVars
  244.         self.bindingTarget=StringVar(self)
  245.         self.builtinKeys=StringVar(self)
  246.         self.customKeys=StringVar(self)
  247.         self.keysAreBuiltin=BooleanVar(self)
  248.         self.keyBinding=StringVar(self)
  249.         ##widget creation
  250.         #body frame
  251.         frame=self.tabPages.pages['Keys']['page']
  252.         #body section frames
  253.         frameCustom=Frame(frame,borderwidth=2,relief=GROOVE)
  254.         frameKeySets=Frame(frame,borderwidth=2,relief=GROOVE)
  255.         #frameCustom
  256.         frameTarget=Frame(frameCustom)
  257.         labelCustomTitle=Label(frameCustom,text='Set Custom Key Bindings')
  258.         labelTargetTitle=Label(frameTarget,text='Action - Key(s)')
  259.         scrollTargetY=Scrollbar(frameTarget)
  260.         scrollTargetX=Scrollbar(frameTarget,orient=HORIZONTAL)
  261.         self.listBindings=Listbox(frameTarget,takefocus=FALSE,
  262.                 exportselection=FALSE)
  263.         self.listBindings.bind('<ButtonRelease-1>',self.KeyBindingSelected)
  264.         scrollTargetY.config(command=self.listBindings.yview)
  265.         scrollTargetX.config(command=self.listBindings.xview)
  266.         self.listBindings.config(yscrollcommand=scrollTargetY.set)
  267.         self.listBindings.config(xscrollcommand=scrollTargetX.set)
  268.         self.buttonNewKeys=Button(frameCustom,text='Get New Keys for Selection',
  269.             command=self.GetNewKeys,state=DISABLED)
  270.         buttonSaveCustomKeys=Button(frameCustom,
  271.                 text='Save as New Custom Key Set',command=self.SaveAsNewKeySet)
  272.         #frameKeySets
  273.         labelKeysTitle=Label(frameKeySets,text='Select a Key Set')
  274.         labelTypeTitle=Label(frameKeySets,text='Select : ')
  275.         self.radioKeysBuiltin=Radiobutton(frameKeySets,variable=self.keysAreBuiltin,
  276.             value=1,command=self.SetKeysType,text='a Built-in Key Set')
  277.         self.radioKeysCustom=Radiobutton(frameKeySets,variable=self.keysAreBuiltin,
  278.             value=0,command=self.SetKeysType,text='a Custom Key Set')
  279.         self.optMenuKeysBuiltin=DynOptionMenu(frameKeySets,
  280.             self.builtinKeys,None,command=None)
  281.         self.optMenuKeysCustom=DynOptionMenu(frameKeySets,
  282.             self.customKeys,None,command=None)
  283.         self.buttonDeleteCustomKeys=Button(frameKeySets,text='Delete Custom Key Set',
  284.                 command=self.DeleteCustomKeys)
  285.         ##widget packing
  286.         #body
  287.         frameCustom.pack(side=LEFT,padx=5,pady=5,expand=TRUE,fill=BOTH)
  288.         frameKeySets.pack(side=LEFT,padx=5,pady=5,fill=Y)
  289.         #frameCustom
  290.         labelCustomTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
  291.         buttonSaveCustomKeys.pack(side=BOTTOM,fill=X,padx=5,pady=5)
  292.         self.buttonNewKeys.pack(side=BOTTOM,fill=X,padx=5,pady=5)
  293.         frameTarget.pack(side=LEFT,padx=5,pady=5,expand=TRUE,fill=BOTH)
  294.         #frame target
  295.         frameTarget.columnconfigure(0,weight=1)
  296.         frameTarget.rowconfigure(1,weight=1)
  297.         labelTargetTitle.grid(row=0,column=0,columnspan=2,sticky=W)
  298.         self.listBindings.grid(row=1,column=0,sticky=NSEW)
  299.         scrollTargetY.grid(row=1,column=1,sticky=NS)
  300.         scrollTargetX.grid(row=2,column=0,sticky=EW)
  301.         #frameKeySets
  302.         labelKeysTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
  303.         labelTypeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
  304.         self.radioKeysBuiltin.pack(side=TOP,anchor=W,padx=5)
  305.         self.radioKeysCustom.pack(side=TOP,anchor=W,padx=5,pady=2)
  306.         self.optMenuKeysBuiltin.pack(side=TOP,fill=X,padx=5,pady=5)
  307.         self.optMenuKeysCustom.pack(side=TOP,fill=X,anchor=W,padx=5,pady=5)
  308.         self.buttonDeleteCustomKeys.pack(side=TOP,fill=X,padx=5,pady=5)
  309.         return frame
  310.  
  311.     def CreatePageGeneral(self):
  312.         #tkVars
  313.         self.winWidth=StringVar(self)
  314.         self.winHeight=StringVar(self)
  315.         self.paraWidth=StringVar(self)
  316.         self.startupEdit=IntVar(self)
  317.         self.autoSave=IntVar(self)
  318.         self.encoding=StringVar(self)
  319.         self.userHelpBrowser=BooleanVar(self)
  320.         self.helpBrowser=StringVar(self)
  321.         #widget creation
  322.         #body
  323.         frame=self.tabPages.pages['General']['page']
  324.         #body section frames
  325.         frameRun=Frame(frame,borderwidth=2,relief=GROOVE)
  326.         frameSave=Frame(frame,borderwidth=2,relief=GROOVE)
  327.         frameWinSize=Frame(frame,borderwidth=2,relief=GROOVE)
  328.         frameParaSize=Frame(frame,borderwidth=2,relief=GROOVE)
  329.         frameEncoding=Frame(frame,borderwidth=2,relief=GROOVE)
  330.         frameHelp=Frame(frame,borderwidth=2,relief=GROOVE)
  331.         #frameRun
  332.         labelRunTitle=Label(frameRun,text='Startup Preferences')
  333.         labelRunChoiceTitle=Label(frameRun,text='At Startup')
  334.         radioStartupEdit=Radiobutton(frameRun,variable=self.startupEdit,
  335.             value=1,command=self.SetKeysType,text="Open Edit Window")
  336.         radioStartupShell=Radiobutton(frameRun,variable=self.startupEdit,
  337.             value=0,command=self.SetKeysType,text='Open Shell Window')
  338.         #frameSave
  339.         labelSaveTitle=Label(frameSave,text='Autosave Preference')
  340.         labelRunSaveTitle=Label(frameSave,text='At Start of Run (F5)  ')
  341.         radioSaveAsk=Radiobutton(frameSave,variable=self.autoSave,
  342.             value=0,command=self.SetKeysType,text="Prompt to Save")
  343.         radioSaveAuto=Radiobutton(frameSave,variable=self.autoSave,
  344.             value=1,command=self.SetKeysType,text='No Prompt')
  345.         #frameWinSize
  346.         labelWinSizeTitle=Label(frameWinSize,text='Initial Window Size'+
  347.                 '  (in characters)')
  348.         labelWinWidthTitle=Label(frameWinSize,text='Width')
  349.         entryWinWidth=Entry(frameWinSize,textvariable=self.winWidth,
  350.                 width=3)
  351.         labelWinHeightTitle=Label(frameWinSize,text='Height')
  352.         entryWinHeight=Entry(frameWinSize,textvariable=self.winHeight,
  353.                 width=3)
  354.         #paragraphFormatWidth
  355.         labelParaWidthTitle=Label(frameParaSize,text='Paragraph reformat'+
  356.                 ' width (in characters)')
  357.         entryParaWidth=Entry(frameParaSize,textvariable=self.paraWidth,
  358.                 width=3)
  359.         #frameEncoding
  360.         labelEncodingTitle=Label(frameEncoding,text="Default Source Encoding")
  361.         radioEncLocale=Radiobutton(frameEncoding,variable=self.encoding,
  362.             value="locale",text="Locale-defined")
  363.         radioEncUTF8=Radiobutton(frameEncoding,variable=self.encoding,
  364.             value="utf-8",text="UTF-8")
  365.         radioEncNone=Radiobutton(frameEncoding,variable=self.encoding,
  366.             value="none",text="None")
  367.         #frameHelp
  368.         frameHelpList=Frame(frameHelp)
  369.         frameHelpListButtons=Frame(frameHelpList)
  370.         labelHelpListTitle=Label(frameHelpList,text='Additional Help Sources:')
  371.         scrollHelpList=Scrollbar(frameHelpList)
  372.         self.listHelp=Listbox(frameHelpList,height=5,takefocus=FALSE,
  373.                 exportselection=FALSE)
  374.         scrollHelpList.config(command=self.listHelp.yview)
  375.         self.listHelp.config(yscrollcommand=scrollHelpList.set)
  376.         self.listHelp.bind('<ButtonRelease-1>',self.HelpSourceSelected)
  377.         self.buttonHelpListEdit=Button(frameHelpListButtons,text='Edit',
  378.                 state=DISABLED,width=8,command=self.HelpListItemEdit)
  379.         self.buttonHelpListAdd=Button(frameHelpListButtons,text='Add',
  380.                 width=8,command=self.HelpListItemAdd)
  381.         self.buttonHelpListRemove=Button(frameHelpListButtons,text='Remove',
  382.                 state=DISABLED,width=8,command=self.HelpListItemRemove)
  383.         #widget packing
  384.         #body
  385.         frameRun.pack(side=TOP,padx=5,pady=5,fill=X)
  386.         frameSave.pack(side=TOP,padx=5,pady=5,fill=X)
  387.         frameWinSize.pack(side=TOP,padx=5,pady=5,fill=X)
  388.         frameParaSize.pack(side=TOP,padx=5,pady=5,fill=X)
  389.         frameEncoding.pack(side=TOP,padx=5,pady=5,fill=X)
  390.         frameHelp.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH)
  391.         #frameRun
  392.         labelRunTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
  393.         labelRunChoiceTitle.pack(side=LEFT,anchor=W,padx=5,pady=5)
  394.         radioStartupShell.pack(side=RIGHT,anchor=W,padx=5,pady=5)
  395.         radioStartupEdit.pack(side=RIGHT,anchor=W,padx=5,pady=5)
  396.         #frameSave
  397.         labelSaveTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
  398.         labelRunSaveTitle.pack(side=LEFT,anchor=W,padx=5,pady=5)
  399.         radioSaveAuto.pack(side=RIGHT,anchor=W,padx=5,pady=5)
  400.         radioSaveAsk.pack(side=RIGHT,anchor=W,padx=5,pady=5)
  401.         #frameWinSize
  402.         labelWinSizeTitle.pack(side=LEFT,anchor=W,padx=5,pady=5)
  403.         entryWinHeight.pack(side=RIGHT,anchor=E,padx=10,pady=5)
  404.         labelWinHeightTitle.pack(side=RIGHT,anchor=E,pady=5)
  405.         entryWinWidth.pack(side=RIGHT,anchor=E,padx=10,pady=5)
  406.         labelWinWidthTitle.pack(side=RIGHT,anchor=E,pady=5)
  407.         #paragraphFormatWidth
  408.         labelParaWidthTitle.pack(side=LEFT,anchor=W,padx=5,pady=5)
  409.         entryParaWidth.pack(side=RIGHT,anchor=E,padx=10,pady=5)
  410.         #frameEncoding
  411.         labelEncodingTitle.pack(side=LEFT,anchor=W,padx=5,pady=5)
  412.         radioEncNone.pack(side=RIGHT,anchor=E,pady=5)
  413.         radioEncUTF8.pack(side=RIGHT,anchor=E,pady=5)
  414.         radioEncLocale.pack(side=RIGHT,anchor=E,pady=5)
  415.         #frameHelp
  416.         frameHelpListButtons.pack(side=RIGHT,padx=5,pady=5,fill=Y)
  417.         frameHelpList.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH)
  418.         labelHelpListTitle.pack(side=TOP,anchor=W)
  419.         scrollHelpList.pack(side=RIGHT,anchor=W,fill=Y)
  420.         self.listHelp.pack(side=LEFT,anchor=E,expand=TRUE,fill=BOTH)
  421.         self.buttonHelpListEdit.pack(side=TOP,anchor=W,pady=5)
  422.         self.buttonHelpListAdd.pack(side=TOP,anchor=W)
  423.         self.buttonHelpListRemove.pack(side=TOP,anchor=W,pady=5)
  424.         return frame
  425.  
  426.     def AttachVarCallbacks(self):
  427.         self.fontSize.trace_variable('w',self.VarChanged_fontSize)
  428.         self.fontName.trace_variable('w',self.VarChanged_fontName)
  429.         self.fontBold.trace_variable('w',self.VarChanged_fontBold)
  430.         self.spaceNum.trace_variable('w',self.VarChanged_spaceNum)
  431.         self.colour.trace_variable('w',self.VarChanged_colour)
  432.         self.builtinTheme.trace_variable('w',self.VarChanged_builtinTheme)
  433.         self.customTheme.trace_variable('w',self.VarChanged_customTheme)
  434.         self.themeIsBuiltin.trace_variable('w',self.VarChanged_themeIsBuiltin)
  435.         self.highlightTarget.trace_variable('w',self.VarChanged_highlightTarget)
  436.         self.keyBinding.trace_variable('w',self.VarChanged_keyBinding)
  437.         self.builtinKeys.trace_variable('w',self.VarChanged_builtinKeys)
  438.         self.customKeys.trace_variable('w',self.VarChanged_customKeys)
  439.         self.keysAreBuiltin.trace_variable('w',self.VarChanged_keysAreBuiltin)
  440.         self.winWidth.trace_variable('w',self.VarChanged_winWidth)
  441.         self.winHeight.trace_variable('w',self.VarChanged_winHeight)
  442.         self.paraWidth.trace_variable('w',self.VarChanged_paraWidth)
  443.         self.startupEdit.trace_variable('w',self.VarChanged_startupEdit)
  444.         self.autoSave.trace_variable('w',self.VarChanged_autoSave)
  445.         self.encoding.trace_variable('w',self.VarChanged_encoding)
  446.  
  447.     def VarChanged_fontSize(self,*params):
  448.         value=self.fontSize.get()
  449.         self.AddChangedItem('main','EditorWindow','font-size',value)
  450.  
  451.     def VarChanged_fontName(self,*params):
  452.         value=self.fontName.get()
  453.         self.AddChangedItem('main','EditorWindow','font',value)
  454.  
  455.     def VarChanged_fontBold(self,*params):
  456.         value=self.fontBold.get()
  457.         self.AddChangedItem('main','EditorWindow','font-bold',value)
  458.  
  459.     def VarChanged_spaceNum(self,*params):
  460.         value=self.spaceNum.get()
  461.         self.AddChangedItem('main','Indent','num-spaces',value)
  462.  
  463.     def VarChanged_colour(self,*params):
  464.         self.OnNewColourSet()
  465.  
  466.     def VarChanged_builtinTheme(self,*params):
  467.         value=self.builtinTheme.get()
  468.         self.AddChangedItem('main','Theme','name',value)
  469.         self.PaintThemeSample()
  470.  
  471.     def VarChanged_customTheme(self,*params):
  472.         value=self.customTheme.get()
  473.         if value != '- no custom themes -':
  474.             self.AddChangedItem('main','Theme','name',value)
  475.             self.PaintThemeSample()
  476.  
  477.     def VarChanged_themeIsBuiltin(self,*params):
  478.         value=self.themeIsBuiltin.get()
  479.         self.AddChangedItem('main','Theme','default',value)
  480.         if value:
  481.             self.VarChanged_builtinTheme()
  482.         else:
  483.             self.VarChanged_customTheme()
  484.  
  485.     def VarChanged_highlightTarget(self,*params):
  486.         self.SetHighlightTarget()
  487.  
  488.     def VarChanged_keyBinding(self,*params):
  489.         value=self.keyBinding.get()
  490.         keySet=self.customKeys.get()
  491.         event=self.listBindings.get(ANCHOR).split()[0]
  492.         if idleConf.IsCoreBinding(event):
  493.             #this is a core keybinding
  494.             self.AddChangedItem('keys',keySet,event,value)
  495.         else: #this is an extension key binding
  496.             extName=idleConf.GetExtnNameForEvent(event)
  497.             extKeybindSection=extName+'_cfgBindings'
  498.             self.AddChangedItem('extensions',extKeybindSection,event,value)
  499.  
  500.     def VarChanged_builtinKeys(self,*params):
  501.         value=self.builtinKeys.get()
  502.         self.AddChangedItem('main','Keys','name',value)
  503.         self.LoadKeysList(value)
  504.  
  505.     def VarChanged_customKeys(self,*params):
  506.         value=self.customKeys.get()
  507.         if value != '- no custom keys -':
  508.             self.AddChangedItem('main','Keys','name',value)
  509.             self.LoadKeysList(value)
  510.  
  511.     def VarChanged_keysAreBuiltin(self,*params):
  512.         value=self.keysAreBuiltin.get()
  513.         self.AddChangedItem('main','Keys','default',value)
  514.         if value:
  515.             self.VarChanged_builtinKeys()
  516.         else:
  517.             self.VarChanged_customKeys()
  518.  
  519.     def VarChanged_winWidth(self,*params):
  520.         value=self.winWidth.get()
  521.         self.AddChangedItem('main','EditorWindow','width',value)
  522.  
  523.     def VarChanged_winHeight(self,*params):
  524.         value=self.winHeight.get()
  525.         self.AddChangedItem('main','EditorWindow','height',value)
  526.  
  527.     def VarChanged_paraWidth(self,*params):
  528.         value=self.paraWidth.get()
  529.         self.AddChangedItem('main','FormatParagraph','paragraph',value)
  530.  
  531.     def VarChanged_startupEdit(self,*params):
  532.         value=self.startupEdit.get()
  533.         self.AddChangedItem('main','General','editor-on-startup',value)
  534.  
  535.     def VarChanged_autoSave(self,*params):
  536.         value=self.autoSave.get()
  537.         self.AddChangedItem('main','General','autosave',value)
  538.  
  539.     def VarChanged_encoding(self,*params):
  540.         value=self.encoding.get()
  541.         self.AddChangedItem('main','EditorWindow','encoding',value)
  542.  
  543.     def ResetChangedItems(self):
  544.         #When any config item is changed in this dialog, an entry
  545.         #should be made in the relevant section (config type) of this
  546.         #dictionary. The key should be the config file section name and the
  547.         #value a dictionary, whose key:value pairs are item=value pairs for
  548.         #that config file section.
  549.         self.changedItems={'main':{},'highlight':{},'keys':{},'extensions':{}}
  550.  
  551.     def AddChangedItem(self,type,section,item,value):
  552.         value=str(value) #make sure we use a string
  553.         if not self.changedItems[type].has_key(section):
  554.             self.changedItems[type][section]={}
  555.         self.changedItems[type][section][item]=value
  556.  
  557.     def GetDefaultItems(self):
  558.         dItems={'main':{},'highlight':{},'keys':{},'extensions':{}}
  559.         for configType in dItems.keys():
  560.             sections=idleConf.GetSectionList('default',configType)
  561.             for section in sections:
  562.                 dItems[configType][section]={}
  563.                 options=idleConf.defaultCfg[configType].GetOptionList(section)
  564.                 for option in options:
  565.                     dItems[configType][section][option]=(
  566.                             idleConf.defaultCfg[configType].Get(section,option))
  567.         return dItems
  568.  
  569.     def SetThemeType(self):
  570.         if self.themeIsBuiltin.get():
  571.             self.optMenuThemeBuiltin.config(state=NORMAL)
  572.             self.optMenuThemeCustom.config(state=DISABLED)
  573.             self.buttonDeleteCustomTheme.config(state=DISABLED)
  574.         else:
  575.             self.optMenuThemeBuiltin.config(state=DISABLED)
  576.             self.radioThemeCustom.config(state=NORMAL)
  577.             self.optMenuThemeCustom.config(state=NORMAL)
  578.             self.buttonDeleteCustomTheme.config(state=NORMAL)
  579.  
  580.     def SetKeysType(self):
  581.         if self.keysAreBuiltin.get():
  582.             self.optMenuKeysBuiltin.config(state=NORMAL)
  583.             self.optMenuKeysCustom.config(state=DISABLED)
  584.             self.buttonDeleteCustomKeys.config(state=DISABLED)
  585.         else:
  586.             self.optMenuKeysBuiltin.config(state=DISABLED)
  587.             self.radioKeysCustom.config(state=NORMAL)
  588.             self.optMenuKeysCustom.config(state=NORMAL)
  589.             self.buttonDeleteCustomKeys.config(state=NORMAL)
  590.  
  591.     def GetNewKeys(self):
  592.         listIndex=self.listBindings.index(ANCHOR)
  593.         binding=self.listBindings.get(listIndex)
  594.         bindName=binding.split()[0] #first part, up to first space
  595.         if self.keysAreBuiltin.get():
  596.             currentKeySetName=self.builtinKeys.get()
  597.         else:
  598.             currentKeySetName=self.customKeys.get()
  599.         currentBindings=idleConf.GetCurrentKeySet()
  600.         if currentKeySetName in self.changedItems['keys'].keys(): #unsaved changes
  601.             keySetChanges=self.changedItems['keys'][currentKeySetName]
  602.             for event in keySetChanges.keys():
  603.                 currentBindings[event]=keySetChanges[event].split()
  604.         currentKeySequences=currentBindings.values()
  605.         newKeys=GetKeysDialog(self,'Get New Keys',bindName,
  606.                 currentKeySequences).result
  607.         if newKeys: #new keys were specified
  608.             if self.keysAreBuiltin.get(): #current key set is a built-in
  609.                 message=('Your changes will be saved as a new Custom Key Set. '+
  610.                         'Enter a name for your new Custom Key Set below.')
  611.                 newKeySet=self.GetNewKeysName(message)
  612.                 if not newKeySet: #user cancelled custom key set creation
  613.                     self.listBindings.select_set(listIndex)
  614.                     self.listBindings.select_anchor(listIndex)
  615.                     return
  616.                 else: #create new custom key set based on previously active key set
  617.                     self.CreateNewKeySet(newKeySet)
  618.             self.listBindings.delete(listIndex)
  619.             self.listBindings.insert(listIndex,bindName+' - '+newKeys)
  620.             self.listBindings.select_set(listIndex)
  621.             self.listBindings.select_anchor(listIndex)
  622.             self.keyBinding.set(newKeys)
  623.         else:
  624.             self.listBindings.select_set(listIndex)
  625.             self.listBindings.select_anchor(listIndex)
  626.  
  627.     def GetNewKeysName(self,message):
  628.         usedNames=(idleConf.GetSectionList('user','keys')+
  629.                 idleConf.GetSectionList('default','keys'))
  630.         newKeySet=GetCfgSectionNameDialog(self,'New Custom Key Set',
  631.                 message,usedNames).result
  632.         return newKeySet
  633.  
  634.     def SaveAsNewKeySet(self):
  635.         newKeysName=self.GetNewKeysName('New Key Set Name:')
  636.         if newKeysName:
  637.             self.CreateNewKeySet(newKeysName)
  638.  
  639.     def KeyBindingSelected(self,event):
  640.         self.buttonNewKeys.config(state=NORMAL)
  641.  
  642.     def CreateNewKeySet(self,newKeySetName):
  643.         #creates new custom key set based on the previously active key set,
  644.         #and makes the new key set active
  645.         if self.keysAreBuiltin.get():
  646.             prevKeySetName=self.builtinKeys.get()
  647.         else:
  648.             prevKeySetName=self.customKeys.get()
  649.         prevKeys=idleConf.GetCoreKeys(prevKeySetName)
  650.         newKeys={}
  651.         for event in prevKeys.keys(): #add key set to changed items
  652.             eventName=event[2:-2] #trim off the angle brackets
  653.             binding=string.join(prevKeys[event])
  654.             newKeys[eventName]=binding
  655.         #handle any unsaved changes to prev key set
  656.         if prevKeySetName in self.changedItems['keys'].keys():
  657.             keySetChanges=self.changedItems['keys'][prevKeySetName]
  658.             for event in keySetChanges.keys():
  659.                 newKeys[event]=keySetChanges[event]
  660.         #save the new theme
  661.         self.SaveNewKeySet(newKeySetName,newKeys)
  662.         #change gui over to the new key set
  663.         customKeyList=idleConf.GetSectionList('user','keys')
  664.         customKeyList.sort()
  665.         self.optMenuKeysCustom.SetMenu(customKeyList,newKeySetName)
  666.         self.keysAreBuiltin.set(0)
  667.         self.SetKeysType()
  668.  
  669.     def LoadKeysList(self,keySetName):
  670.         reselect=0
  671.         newKeySet=0
  672.         if self.listBindings.curselection():
  673.             reselect=1
  674.             listIndex=self.listBindings.index(ANCHOR)
  675.         keySet=idleConf.GetKeySet(keySetName)
  676.         bindNames=keySet.keys()
  677.         bindNames.sort()
  678.         self.listBindings.delete(0,END)
  679.         for bindName in bindNames:
  680.             key=string.join(keySet[bindName]) #make key(s) into a string
  681.             bindName=bindName[2:-2] #trim off the angle brackets
  682.             if keySetName in self.changedItems['keys'].keys():
  683.                 #handle any unsaved changes to this key set
  684.                 if bindName in self.changedItems['keys'][keySetName].keys():
  685.                     key=self.changedItems['keys'][keySetName][bindName]
  686.             self.listBindings.insert(END, bindName+' - '+key)
  687.         if reselect:
  688.             self.listBindings.see(listIndex)
  689.             self.listBindings.select_set(listIndex)
  690.             self.listBindings.select_anchor(listIndex)
  691.  
  692.     def DeleteCustomKeys(self):
  693.         keySetName=self.customKeys.get()
  694.         if not tkMessageBox.askyesno('Delete Key Set','Are you sure you wish '+
  695.                                      'to delete the key set %r ?' % (keySetName),
  696.                                      parent=self):
  697.             return
  698.         #remove key set from config
  699.         idleConf.userCfg['keys'].remove_section(keySetName)
  700.         if self.changedItems['keys'].has_key(keySetName):
  701.             del(self.changedItems['keys'][keySetName])
  702.         #write changes
  703.         idleConf.userCfg['keys'].Save()
  704.         #reload user key set list
  705.         itemList=idleConf.GetSectionList('user','keys')
  706.         itemList.sort()
  707.         if not itemList:
  708.             self.radioKeysCustom.config(state=DISABLED)
  709.             self.optMenuKeysCustom.SetMenu(itemList,'- no custom keys -')
  710.         else:
  711.             self.optMenuKeysCustom.SetMenu(itemList,itemList[0])
  712.         #revert to default key set
  713.         self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys','default'))
  714.         self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys','name'))
  715.         #user can't back out of these changes, they must be applied now
  716.         self.Apply()
  717.         self.SetKeysType()
  718.  
  719.     def DeleteCustomTheme(self):
  720.         themeName=self.customTheme.get()
  721.         if not tkMessageBox.askyesno('Delete Theme','Are you sure you wish '+
  722.                                      'to delete the theme %r ?' % (themeName,),
  723.                                      parent=self):
  724.             return
  725.         #remove theme from config
  726.         idleConf.userCfg['highlight'].remove_section(themeName)
  727.         if self.changedItems['highlight'].has_key(themeName):
  728.             del(self.changedItems['highlight'][themeName])
  729.         #write changes
  730.         idleConf.userCfg['highlight'].Save()
  731.         #reload user theme list
  732.         itemList=idleConf.GetSectionList('user','highlight')
  733.         itemList.sort()
  734.         if not itemList:
  735.             self.radioThemeCustom.config(state=DISABLED)
  736.             self.optMenuThemeCustom.SetMenu(itemList,'- no custom themes -')
  737.         else:
  738.             self.optMenuThemeCustom.SetMenu(itemList,itemList[0])
  739.         #revert to default theme
  740.         self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme','default'))
  741.         self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme','name'))
  742.         #user can't back out of these changes, they must be applied now
  743.         self.Apply()
  744.         self.SetThemeType()
  745.  
  746.     def GetColour(self):
  747.         target=self.highlightTarget.get()
  748.         prevColour=self.frameColourSet.cget('bg')
  749.         rgbTuplet, colourString = tkColorChooser.askcolor(parent=self,
  750.             title='Pick new colour for : '+target,initialcolor=prevColour)
  751.         if colourString and (colourString!=prevColour):
  752.             #user didn't cancel, and they chose a new colour
  753.             if self.themeIsBuiltin.get(): #current theme is a built-in
  754.                 message=('Your changes will be saved as a new Custom Theme. '+
  755.                         'Enter a name for your new Custom Theme below.')
  756.                 newTheme=self.GetNewThemeName(message)
  757.                 if not newTheme: #user cancelled custom theme creation
  758.                     return
  759.                 else: #create new custom theme based on previously active theme
  760.                     self.CreateNewTheme(newTheme)
  761.                     self.colour.set(colourString)
  762.             else: #current theme is user defined
  763.                 self.colour.set(colourString)
  764.  
  765.     def OnNewColourSet(self):
  766.         newColour=self.colour.get()
  767.         self.frameColourSet.config(bg=newColour)#set sample
  768.         if self.fgHilite.get(): plane='foreground'
  769.         else: plane='background'
  770.         sampleElement=self.themeElements[self.highlightTarget.get()][0]
  771.         self.textHighlightSample.tag_config(sampleElement, **{plane:newColour})
  772.         theme=self.customTheme.get()
  773.         themeElement=sampleElement+'-'+plane
  774.         self.AddChangedItem('highlight',theme,themeElement,newColour)
  775.  
  776.     def GetNewThemeName(self,message):
  777.         usedNames=(idleConf.GetSectionList('user','highlight')+
  778.                 idleConf.GetSectionList('default','highlight'))
  779.         newTheme=GetCfgSectionNameDialog(self,'New Custom Theme',
  780.                 message,usedNames).result
  781.         return newTheme
  782.  
  783.     def SaveAsNewTheme(self):
  784.         newThemeName=self.GetNewThemeName('New Theme Name:')
  785.         if newThemeName:
  786.             self.CreateNewTheme(newThemeName)
  787.  
  788.     def CreateNewTheme(self,newThemeName):
  789.         #creates new custom theme based on the previously active theme,
  790.         #and makes the new theme active
  791.         if self.themeIsBuiltin.get():
  792.             themeType='default'
  793.             themeName=self.builtinTheme.get()
  794.         else:
  795.             themeType='user'
  796.             themeName=self.customTheme.get()
  797.         newTheme=idleConf.GetThemeDict(themeType,themeName)
  798.         #apply any of the old theme's unsaved changes to the new theme
  799.         if themeName in self.changedItems['highlight'].keys():
  800.             themeChanges=self.changedItems['highlight'][themeName]
  801.             for element in themeChanges.keys():
  802.                 newTheme[element]=themeChanges[element]
  803.         #save the new theme
  804.         self.SaveNewTheme(newThemeName,newTheme)
  805.         #change gui over to the new theme
  806.         customThemeList=idleConf.GetSectionList('user','highlight')
  807.         customThemeList.sort()
  808.         self.optMenuThemeCustom.SetMenu(customThemeList,newThemeName)
  809.         self.themeIsBuiltin.set(0)
  810.         self.SetThemeType()
  811.  
  812.     def OnListFontButtonRelease(self,event):
  813.         font = self.listFontName.get(ANCHOR)
  814.         self.fontName.set(font.lower())
  815.         self.SetFontSample()
  816.  
  817.     def SetFontSample(self,event=None):
  818.         fontName=self.fontName.get()
  819.         if self.fontBold.get():
  820.             fontWeight=tkFont.BOLD
  821.         else:
  822.             fontWeight=tkFont.NORMAL
  823.         self.editFont.config(size=self.fontSize.get(),
  824.                 weight=fontWeight,family=fontName)
  825.  
  826.     def SetHighlightTarget(self):
  827.         if self.highlightTarget.get()=='Cursor': #bg not possible
  828.             self.radioFg.config(state=DISABLED)
  829.             self.radioBg.config(state=DISABLED)
  830.             self.fgHilite.set(1)
  831.         else: #both fg and bg can be set
  832.             self.radioFg.config(state=NORMAL)
  833.             self.radioBg.config(state=NORMAL)
  834.             self.fgHilite.set(1)
  835.         self.SetColourSample()
  836.  
  837.     def SetColourSampleBinding(self,*args):
  838.         self.SetColourSample()
  839.  
  840.     def SetColourSample(self):
  841.         #set the colour smaple area
  842.         tag=self.themeElements[self.highlightTarget.get()][0]
  843.         if self.fgHilite.get(): plane='foreground'
  844.         else: plane='background'
  845.         colour=self.textHighlightSample.tag_cget(tag,plane)
  846.         self.frameColourSet.config(bg=colour)
  847.  
  848.     def PaintThemeSample(self):
  849.         if self.themeIsBuiltin.get(): #a default theme
  850.             theme=self.builtinTheme.get()
  851.         else: #a user theme
  852.             theme=self.customTheme.get()
  853.         for elementTitle in self.themeElements.keys():
  854.             element=self.themeElements[elementTitle][0]
  855.             colours=idleConf.GetHighlight(theme,element)
  856.             if element=='cursor': #cursor sample needs special painting
  857.                 colours['background']=idleConf.GetHighlight(theme,
  858.                         'normal', fgBg='bg')
  859.             #handle any unsaved changes to this theme
  860.             if theme in self.changedItems['highlight'].keys():
  861.                 themeDict=self.changedItems['highlight'][theme]
  862.                 if themeDict.has_key(element+'-foreground'):
  863.                     colours['foreground']=themeDict[element+'-foreground']
  864.                 if themeDict.has_key(element+'-background'):
  865.                     colours['background']=themeDict[element+'-background']
  866.             self.textHighlightSample.tag_config(element, **colours)
  867.         self.SetColourSample()
  868.  
  869.     def HelpSourceSelected(self,event):
  870.         self.SetHelpListButtonStates()
  871.  
  872.     def SetHelpListButtonStates(self):
  873.         if self.listHelp.size()<1: #no entries in list
  874.             self.buttonHelpListEdit.config(state=DISABLED)
  875.             self.buttonHelpListRemove.config(state=DISABLED)
  876.         else: #there are some entries
  877.             if self.listHelp.curselection(): #there currently is a selection
  878.                 self.buttonHelpListEdit.config(state=NORMAL)
  879.                 self.buttonHelpListRemove.config(state=NORMAL)
  880.             else:  #there currently is not a selection
  881.                 self.buttonHelpListEdit.config(state=DISABLED)
  882.                 self.buttonHelpListRemove.config(state=DISABLED)
  883.  
  884.     def HelpListItemAdd(self):
  885.         helpSource=GetHelpSourceDialog(self,'New Help Source').result
  886.         if helpSource:
  887.             self.userHelpList.append( (helpSource[0],helpSource[1]) )
  888.             self.listHelp.insert(END,helpSource[0])
  889.             self.UpdateUserHelpChangedItems()
  890.         self.SetHelpListButtonStates()
  891.  
  892.     def HelpListItemEdit(self):
  893.         itemIndex=self.listHelp.index(ANCHOR)
  894.         helpSource=self.userHelpList[itemIndex]
  895.         newHelpSource=GetHelpSourceDialog(self,'Edit Help Source',
  896.                 menuItem=helpSource[0],filePath=helpSource[1]).result
  897.         if (not newHelpSource) or (newHelpSource==helpSource):
  898.             return #no changes
  899.         self.userHelpList[itemIndex]=newHelpSource
  900.         self.listHelp.delete(itemIndex)
  901.         self.listHelp.insert(itemIndex,newHelpSource[0])
  902.         self.UpdateUserHelpChangedItems()
  903.         self.SetHelpListButtonStates()
  904.  
  905.     def HelpListItemRemove(self):
  906.         itemIndex=self.listHelp.index(ANCHOR)
  907.         del(self.userHelpList[itemIndex])
  908.         self.listHelp.delete(itemIndex)
  909.         self.UpdateUserHelpChangedItems()
  910.         self.SetHelpListButtonStates()
  911.  
  912.     def UpdateUserHelpChangedItems(self):
  913.         "Clear and rebuild the HelpFiles section in self.changedItems"
  914.         self.changedItems['main']['HelpFiles'] = {}
  915.         for num in range(1,len(self.userHelpList)+1):
  916.             self.AddChangedItem('main','HelpFiles',str(num),
  917.                     string.join(self.userHelpList[num-1][:2],';'))
  918.  
  919.     def LoadFontCfg(self):
  920.         ##base editor font selection list
  921.         fonts=list(tkFont.families(self))
  922.         fonts.sort()
  923.         for font in fonts:
  924.             self.listFontName.insert(END,font)
  925.         configuredFont=idleConf.GetOption('main','EditorWindow','font',
  926.                 default='courier')
  927.         lc_configuredFont = configuredFont.lower()
  928.         self.fontName.set(lc_configuredFont)
  929.         lc_fonts = [s.lower() for s in fonts]
  930.         if lc_configuredFont in lc_fonts:
  931.             currentFontIndex = lc_fonts.index(lc_configuredFont)
  932.             self.listFontName.see(currentFontIndex)
  933.             self.listFontName.select_set(currentFontIndex)
  934.             self.listFontName.select_anchor(currentFontIndex)
  935.         ##font size dropdown
  936.         fontSize=idleConf.GetOption('main','EditorWindow','font-size',
  937.                 default='10')
  938.         self.optMenuFontSize.SetMenu(('7','8','9','10','11','12','13','14',
  939.                 '16','18','20','22'),fontSize )
  940.         ##fontWeight
  941.         self.fontBold.set(idleConf.GetOption('main','EditorWindow',
  942.                 'font-bold',default=0,type='bool'))
  943.         ##font sample
  944.         self.SetFontSample()
  945.  
  946.     def LoadTabCfg(self):
  947.         ##indent sizes
  948.         spaceNum=idleConf.GetOption('main','Indent','num-spaces',
  949.                 default=4,type='int')
  950.         self.spaceNum.set(spaceNum)
  951.  
  952.     def LoadThemeCfg(self):
  953.         ##current theme type radiobutton
  954.         self.themeIsBuiltin.set(idleConf.GetOption('main','Theme','default',
  955.             type='bool',default=1))
  956.         ##currently set theme
  957.         currentOption=idleConf.CurrentTheme()
  958.         ##load available theme option menus
  959.         if self.themeIsBuiltin.get(): #default theme selected
  960.             itemList=idleConf.GetSectionList('default','highlight')
  961.             itemList.sort()
  962.             self.optMenuThemeBuiltin.SetMenu(itemList,currentOption)
  963.             itemList=idleConf.GetSectionList('user','highlight')
  964.             itemList.sort()
  965.             if not itemList:
  966.                 self.radioThemeCustom.config(state=DISABLED)
  967.                 self.customTheme.set('- no custom themes -')
  968.             else:
  969.                 self.optMenuThemeCustom.SetMenu(itemList,itemList[0])
  970.         else: #user theme selected
  971.             itemList=idleConf.GetSectionList('user','highlight')
  972.             itemList.sort()
  973.             self.optMenuThemeCustom.SetMenu(itemList,currentOption)
  974.             itemList=idleConf.GetSectionList('default','highlight')
  975.             itemList.sort()
  976.             self.optMenuThemeBuiltin.SetMenu(itemList,itemList[0])
  977.         self.SetThemeType()
  978.         ##load theme element option menu
  979.         themeNames=self.themeElements.keys()
  980.         themeNames.sort(self.__ThemeNameIndexCompare)
  981.         self.optMenuHighlightTarget.SetMenu(themeNames,themeNames[0])
  982.         self.PaintThemeSample()
  983.         self.SetHighlightTarget()
  984.  
  985.     def __ThemeNameIndexCompare(self,a,b):
  986.         if self.themeElements[a][1]<self.themeElements[b][1]: return -1
  987.         elif self.themeElements[a][1]==self.themeElements[b][1]: return 0
  988.         else: return 1
  989.  
  990.     def LoadKeyCfg(self):
  991.         ##current keys type radiobutton
  992.         self.keysAreBuiltin.set(idleConf.GetOption('main','Keys','default',
  993.             type='bool',default=1))
  994.         ##currently set keys
  995.         currentOption=idleConf.CurrentKeys()
  996.         ##load available keyset option menus
  997.         if self.keysAreBuiltin.get(): #default theme selected
  998.             itemList=idleConf.GetSectionList('default','keys')
  999.             itemList.sort()
  1000.             self.optMenuKeysBuiltin.SetMenu(itemList,currentOption)
  1001.             itemList=idleConf.GetSectionList('user','keys')
  1002.             itemList.sort()
  1003.             if not itemList:
  1004.                 self.radioKeysCustom.config(state=DISABLED)
  1005.                 self.customKeys.set('- no custom keys -')
  1006.             else:
  1007.                 self.optMenuKeysCustom.SetMenu(itemList,itemList[0])
  1008.         else: #user key set selected
  1009.             itemList=idleConf.GetSectionList('user','keys')
  1010.             itemList.sort()
  1011.             self.optMenuKeysCustom.SetMenu(itemList,currentOption)
  1012.             itemList=idleConf.GetSectionList('default','keys')
  1013.             itemList.sort()
  1014.             self.optMenuKeysBuiltin.SetMenu(itemList,itemList[0])
  1015.         self.SetKeysType()
  1016.         ##load keyset element list
  1017.         keySetName=idleConf.CurrentKeys()
  1018.         self.LoadKeysList(keySetName)
  1019.  
  1020.     def LoadGeneralCfg(self):
  1021.         #startup state
  1022.         self.startupEdit.set(idleConf.GetOption('main','General',
  1023.                 'editor-on-startup',default=1,type='bool'))
  1024.         #autosave state
  1025.         self.autoSave.set(idleConf.GetOption('main', 'General', 'autosave',
  1026.                                              default=0, type='bool'))
  1027.         #initial window size
  1028.         self.winWidth.set(idleConf.GetOption('main','EditorWindow','width'))
  1029.         self.winHeight.set(idleConf.GetOption('main','EditorWindow','height'))
  1030.         #initial paragraph reformat size
  1031.         self.paraWidth.set(idleConf.GetOption('main','FormatParagraph','paragraph'))
  1032.         # default source encoding
  1033.         self.encoding.set(idleConf.GetOption('main', 'EditorWindow',
  1034.                                              'encoding', default='none'))
  1035.         # additional help sources
  1036.         self.userHelpList = idleConf.GetAllExtraHelpSourcesList()
  1037.         for helpItem in self.userHelpList:
  1038.             self.listHelp.insert(END,helpItem[0])
  1039.         self.SetHelpListButtonStates()
  1040.  
  1041.     def LoadConfigs(self):
  1042.         """
  1043.         load configuration from default and user config files and populate
  1044.         the widgets on the config dialog pages.
  1045.         """
  1046.         ### fonts / tabs page
  1047.         self.LoadFontCfg()
  1048.         self.LoadTabCfg()
  1049.         ### highlighting page
  1050.         self.LoadThemeCfg()
  1051.         ### keys page
  1052.         self.LoadKeyCfg()
  1053.         ### general page
  1054.         self.LoadGeneralCfg()
  1055.  
  1056.     def SaveNewKeySet(self,keySetName,keySet):
  1057.         """
  1058.         save a newly created core key set.
  1059.         keySetName - string, the name of the new key set
  1060.         keySet - dictionary containing the new key set
  1061.         """
  1062.         if not idleConf.userCfg['keys'].has_section(keySetName):
  1063.             idleConf.userCfg['keys'].add_section(keySetName)
  1064.         for event in keySet.keys():
  1065.             value=keySet[event]
  1066.             idleConf.userCfg['keys'].SetOption(keySetName,event,value)
  1067.  
  1068.     def SaveNewTheme(self,themeName,theme):
  1069.         """
  1070.         save a newly created theme.
  1071.         themeName - string, the name of the new theme
  1072.         theme - dictionary containing the new theme
  1073.         """
  1074.         if not idleConf.userCfg['highlight'].has_section(themeName):
  1075.             idleConf.userCfg['highlight'].add_section(themeName)
  1076.         for element in theme.keys():
  1077.             value=theme[element]
  1078.             idleConf.userCfg['highlight'].SetOption(themeName,element,value)
  1079.  
  1080.     def SetUserValue(self,configType,section,item,value):
  1081.         if idleConf.defaultCfg[configType].has_option(section,item):
  1082.             if idleConf.defaultCfg[configType].Get(section,item)==value:
  1083.                 #the setting equals a default setting, remove it from user cfg
  1084.                 return idleConf.userCfg[configType].RemoveOption(section,item)
  1085.         #if we got here set the option
  1086.         return idleConf.userCfg[configType].SetOption(section,item,value)
  1087.  
  1088.     def SaveAllChangedConfigs(self):
  1089.         "Save configuration changes to the user config file."
  1090.         idleConf.userCfg['main'].Save()
  1091.         for configType in self.changedItems.keys():
  1092.             cfgTypeHasChanges = False
  1093.             for section in self.changedItems[configType].keys():
  1094.                 if section == 'HelpFiles':
  1095.                     #this section gets completely replaced
  1096.                     idleConf.userCfg['main'].remove_section('HelpFiles')
  1097.                     cfgTypeHasChanges = True
  1098.                 for item in self.changedItems[configType][section].keys():
  1099.                     value = self.changedItems[configType][section][item]
  1100.                     if self.SetUserValue(configType,section,item,value):
  1101.                         cfgTypeHasChanges = True
  1102.             if cfgTypeHasChanges:
  1103.                 idleConf.userCfg[configType].Save()
  1104.         for configType in ['keys', 'highlight']:
  1105.             # save these even if unchanged!
  1106.             idleConf.userCfg[configType].Save()
  1107.         self.ResetChangedItems() #clear the changed items dict
  1108.  
  1109.     def DeactivateCurrentConfig(self):
  1110.         #Before a config is saved, some cleanup of current
  1111.         #config must be done - remove the previous keybindings
  1112.         winInstances=self.parent.instance_dict.keys()
  1113.         for instance in winInstances:
  1114.             instance.RemoveKeybindings()
  1115.  
  1116.     def ActivateConfigChanges(self):
  1117.         "Dynamically apply configuration changes"
  1118.         winInstances=self.parent.instance_dict.keys()
  1119.         for instance in winInstances:
  1120.             instance.ResetColorizer()
  1121.             instance.ResetFont()
  1122.             instance.set_notabs_indentwidth()
  1123.             instance.ApplyKeybindings()
  1124.             instance.reset_help_menu_entries()
  1125.  
  1126.     def Cancel(self):
  1127.         self.destroy()
  1128.  
  1129.     def Ok(self):
  1130.         self.Apply()
  1131.         self.destroy()
  1132.  
  1133.     def Apply(self):
  1134.         self.DeactivateCurrentConfig()
  1135.         self.SaveAllChangedConfigs()
  1136.         self.ActivateConfigChanges()
  1137.  
  1138.     def Help(self):
  1139.         pass
  1140.  
  1141. if __name__ == '__main__':
  1142.     #test the dialog
  1143.     root=Tk()
  1144.     Button(root,text='Dialog',
  1145.             command=lambda:ConfigDialog(root,'Settings')).pack()
  1146.     root.instance_dict={}
  1147.     root.mainloop()
  1148.